import java.sql.*;
import java.util.*;

public class Apriori extends Thread {
	
	public static boolean debugger = false;
	public static ArrayList<Double> support = new ArrayList<Double>();
	public static List<List<String>> frequent=new ArrayList<List<String>>();;
	public static double minsup = 0.20;
	public static double minconf = 0.50;
	public static ArrayList<String> item;
	public static Statement statement;
	public static int db_size;
	public static ArrayList<List<String>> Fk1 = new ArrayList<List<String>>();
	public static ArrayList<String> unique_itemset = new ArrayList<String>();
	public static ArrayList<String> temp = new ArrayList<String>();
	public static ArrayList<String> pattern = new ArrayList<String>();
	public static ArrayList<String> list = new ArrayList<String>();
	public static ArrayList<Integer> count_kth = new ArrayList<Integer>();
	public static ArrayList<String> spaltenname = new ArrayList<String>();
	public static Integer count;
	public static Double supp;
	public static Double conf;
	public static Integer c;
	public static Integer c1;

	public static void main(String args[]) throws Exception {
		Connection connection = null;
		Statement statement = null;

		String url = "jdbc:postgresql://localhost:5432/";
		String db = "CATDATDB";
		String driver = "org.postgresql.Driver";
		String user = "postgres";
		String pass = "dast";

		try {
			Class.forName(driver); 
			connection = DriverManager.getConnection(url + db, user, pass); 
																			

			statement = connection.createStatement(); 
			
			column_names(statement);
			unique_itemset(statement);
			db_size(statement);

			// FileLogger.main(args);

			System.out.println("\nStarting Apriori");

			Apriori test1 = null;
			try {
				test1 = new Apriori(unique_itemset, db_size);

			} catch (Exception e1) {
				e1.printStackTrace();
			}
			Apriori.debugger = true;
			test1.start();
			try {
				test1.join();
				test1.printPatterns();
				test1.confFinal(statement);
			} catch (Exception e) {
				e.printStackTrace();
			}

		} catch (Exception e) {
			System.out.println(e);
		}

	}

	public Apriori(ArrayList<String> unique_itemset, Integer db_size)
			throws Exception {
		
	}

	@Override
	public void run() {

		double startTime = System.currentTimeMillis();
		int k = 1;

		HashMap<List<String>, Integer> seenK = new HashMap<List<String>, Integer>();
		List<List<String>> Ck = new ArrayList<List<String>>();
		List<List<String>> Lk = new ArrayList<List<String>>();
		Connection connection = null;
		Statement statement = null;

		String url = "jdbc:postgresql://localhost:5432/";
		String db = "CATDATDB";
		String driver = "org.postgresql.Driver";
		String user = "postgres";
		String pass = "dast";

		try {
			Class.forName(driver);
			connection = DriverManager.getConnection(url + db, user, pass);

			statement = connection.createStatement();

			for (String item : unique_itemset) {
				List<String> temp = new ArrayList<String>();
				temp.add(item);
				Lk.add(temp);
			}

			while (k <= db_size && !Lk.isEmpty()) {
				if (debugger) {
					System.out.print("\n");
					System.out.println();
					System.out.println("Step " + k);
					System.out.print("\n");
					System.out.println("Lk: " + Lk);

				}
				seenK.clear();
				Ck.clear();

				for (List<String> kth : Lk) {

					c = countFinal(statement, kth);

					double support = supp(statement, c);

					if (support <= minsup)
						continue;
					Ck.add(kth);

				}

				if (debugger) {
					System.out.print("\n");
					System.out.println("Ck: " + Ck);
				}
				if (Ck.isEmpty())
					break;

				for (List<String> freq : Ck) {
					frequent.add(freq);
					seenK.put(freq, k);
				}

				int[] prefixlen = new int[Ck.size()];
				prefixlen[0] = 0;
				for (int i = 1; i < Ck.size(); i++) {
					prefixlen[i] = prefixLen(Ck.get(i - 1), Ck.get(i));
				}
				List<List<String>> temp = new ArrayList<List<String>>();
				for (int i = 0; i < Ck.size(); i++) {
					for (int j = i + 1; j < Ck.size(); j++) {
						if (prefixlen[j] == k - 1) {
							if (debugger) {
								System.out.println("Joining: " + i + ":"
										+ Ck.get(i) + " + " + j + ":"
										+ Ck.get(j) + " Prefix Length "
										+ prefixlen[j]);
							}
							temp.add(prefixJoin(Ck.get(i), Ck.get(j)));

						} else
							break;
					}
				}
				if (debugger) {

					System.out.println("Temporary: " + temp);
				}

				Lk.clear();

				for (List<String> list : temp) {
					boolean candid = true;
					if (k > 1) {
						for (int i = 0; i < list.size(); i++) {
							ArrayList<String> prev = new ArrayList<String>();
							for (int j = 0; j < list.size(); j++) {
								if (i != j)
									prev.add(list.get(j));
							}
							if (!seenK.containsKey(prev)) {
								candid = false;
								break;
							}
						}
					}
					if (candid) {
						Lk.add(list);

					}

				}
				if (debugger) {
					System.out.print(" ");
					System.out.println("Pruned: " + Lk);
				}

				k++;

			}
			double endTime = System.currentTimeMillis();
			System.out.print("\n");
			System.out.println("Apriori completed in " + (endTime - startTime)
					/ 1000.0 + " seconds");
		} catch (Exception e) {
			System.out.println(e);
		}

	}

	public void printPatterns() {
		System.out.print("\n");
		System.out.println();
		System.out.println("Frequent Itemsets - minsup = " + minsup);
		System.out.println();
		for (List<String> pattern : frequent) {
			System.out.println(pattern);
		}
		System.out.print("\n");
		System.out.println("Total " + frequent.size() + " itemsets");
	}

	public int prefixLen(List<String> left, List<String> right) {
		int len = 0;
		for (len = 0; len < left.size() && len < right.size(); len++) {
			if (left.get(len).compareTo(right.get(len)) != 0)
				return len;
		}
		return len;
	}

	public List<String> prefixJoin(List<String> left, List<String> right) {
		List<String> ret = new ArrayList<String>();
		for (String i : left) {
			ret.add(i);
		}
		ret.add(right.get(right.size() - 1));
		return ret;
	}

	public Double supp(Statement new_statement, int count) {

		double support_value = (double) count / (double) db_size;
		double round_support_value = Math.round(support_value * 10000) / 10000.0; 

		supp = round_support_value;

		return supp;
	}

	public int countFinal(Statement new_statement, List<String> kth)
			throws Exception {
		statement = new_statement;

		ColumnMatch match = new ColumnMatch();
		String column = "";
		String query = "";
		String selectQuery = "SELECT Count (*) FROM iket.damage, iket.event_earthquake, iket.location_data WHERE iket.damage.event_id=iket.event_earthquake.event_id AND iket.damage.location_id=iket.location_data.location_id ";
		String completeQuery = "";

		for (String s : kth) {
			column = match.assign(s);
			query += " AND " + column + "=" + "'" + s + "'";
		}

		completeQuery = selectQuery + query;

		ResultSet result5 = statement.executeQuery(completeQuery);

		while (result5.next())

			c = result5.getInt(1);

		return c;

	}

	public void confFinal(Statement new_statement) throws Exception {

		statement = new_statement;
		int count_rules = 0;

		System.out.print("\n");
		System.out.println();
		System.out.println("Association Rules - minconf = " + minconf);

		for (int i = 0; i <= frequent.size() - 1; i++) {

			List<String> kth = frequent.get(i);
			int ks = kth.size();
			if (ks == 1)
				continue;

			Set<Set<String>> test = new HashSet<Set<String>>();
			ArrayList ids = new ArrayList();
			test = potenzMenge(kth);
			List<Set<String>> itemSetList = new ArrayList<Set<String>>(test);
			Iterator<Set<String>> iter = test.iterator();
			int k = 0;
			while (iter.hasNext()) {
				Set<String> temp = iter.next();
				if (temp.size() == 0 || temp.size() == kth.size()) {
					ids.add(k);

				}

				k++;
			}

			for (int z = ids.size() - 1; z >= 0; z--) {
				int w = (int) ids.get(z);
				itemSetList.remove(w);

			}

			/*
			 * System.out.println("neue Menge"); for (Set<String> x :
			 * itemSetList) { System.out.println(x.toString()); }
			 */

			ColumnMatch match = new ColumnMatch();
			String column = "";
			String query = "";
			String query_s = "";
			String selectQuery = "SELECT Count (*) FROM iket.damage, iket.event_earthquake, iket.location_data WHERE iket.damage.event_id=iket.event_earthquake.event_id AND iket.damage.location_id=iket.location_data.location_id ";
			String completeQuery = "";
			String confQuery = "";

			for (String s : kth) {
				column = match.assign(s);
				query += " AND " + column + "=" + "'" + s + "'";
			}

			completeQuery = selectQuery + query;

			ResultSet result5 = statement.executeQuery(completeQuery);

			while (result5.next())

				c = result5.getInt(1);

			for (int j = 0; j < itemSetList.size(); j++) {

				List<String> itemSetList2 = new ArrayList<String>(itemSetList.get(j));
				query_s = "";
				for (int p = 0; p < itemSetList2.size(); p++) {
					column = match.assign(itemSetList2.get(p));
					query_s += " AND " + column + "=" + "'"
							+ itemSetList2.get(p) + "'";

				}

				confQuery = selectQuery + query_s;
				ResultSet result6 = statement.executeQuery(confQuery);

				while (result6.next())

					c1 = result6.getInt(1);

				double conf1 = (double) c / (double) c1;
				double round_conf1 = Math.round(conf1 * 100) / 100.0;

				if (conf1 < minconf)
					continue;

				String kthString2 = "";

				for (int n = 0; n < itemSetList2.size(); n++) {
					kthString2 += itemSetList2.get(n) + ", ";
				}

				System.out.println();
				System.out.print(kthString2 + " --> " + kth + " (Confidence: "
						+ round_conf1 + ") ");
				count_rules = count_rules + 1;

			}
		}

		System.out.println("\n");
		System.out.println("Total " + count_rules + " rules");
	}

	protected static Set<Set<String>> potenzMenge(List<String> mengeOld) {
		Set<String> menge = new HashSet<String>(mengeOld);

		Set<Set<String>> potenzmenge = new HashSet<Set<String>>();
		potenzmenge.add(menge);
		for (String string : menge) {
			Set<String> newset = new HashSet<String>();
			newset.add(string);
			// System.out.println("added: [" + string + "]");

			potenzmenge.add(newset);
		}

		for (String string : menge) {
			Set<Set<String>> neu = berechneTeilmenge(potenzmenge, string);
			for (Set<String> set : neu) {
				potenzmenge.add(set);
			}
		}

		/* Ausgabe zum testen */
		/*
		 * System.out.println("\n\nPotenzmenge:"); for (Set<String> x :
		 * potenzmenge) { System.out.println(x.toString()); }
		 */

		return potenzmenge;
	}

	public static Set<Set<String>> berechneTeilmenge(
			Set<Set<String>> potenzmenge, String s) {
		Set<Set<String>> calc = new HashSet<Set<String>>();
		for (Set<String> set : potenzmenge) {
			Set<String> toAdd = new HashSet<String>();

			if (!set.contains(s)) {
				toAdd.addAll(set);
				toAdd.add(s);
			}
			calc.add(toAdd);
		}

		return calc;
	}

	public static ArrayList<String> column_names(Statement new_statement) 
			throws Exception {

		statement = new_statement;

		String query = "SELECT casualties_shaking_Klasse, homeless_Klasse, casualties_non_shaking_Klasse, casualties_Klasse, direct_loss_Klasse, damaged_buildings_Klasse, destroyed_buildings_Klasse, total_loss_Klasse, catdat_upper_deaths_Klasse, catdat_lower_deaths_Klasse, catdat_median_ecloss_evyear_Klasse, catdat_upper_ecloss_evyear_Klasse, catdat_lower_ecloss_evyear_Klasse, injured_sum_Klasse, depth_Klasse, eq_magnitude_Klasse, hdi_Klasse FROM iket.damage, iket.event_earthquake, iket.location_data WHERE iket.damage.event_id=iket.event_earthquake.event_id AND iket.damage.location_id=iket.location_data.location_id";
		ResultSet result = statement.executeQuery(query);

		ResultSetMetaData resultmd = result.getMetaData();
		int columnCount = resultmd.getColumnCount();

		for (int i = 1; i <= columnCount; i++) {

			spaltenname.add(resultmd.getColumnName(i));

		}

		return spaltenname;
	}

	public static ArrayList<String> unique_itemset(Statement new_statement) 
																			
			throws Exception {

		statement = new_statement;

		String query = "SELECT casualties_shaking_Klasse, homeless_Klasse, casualties_non_shaking_Klasse, casualties_Klasse, direct_loss_Klasse, damaged_buildings_Klasse, destroyed_buildings_Klasse, total_loss_Klasse, catdat_upper_deaths_Klasse, catdat_lower_deaths_Klasse, catdat_median_ecloss_evyear_Klasse, catdat_upper_ecloss_evyear_Klasse, catdat_lower_ecloss_evyear_Klasse, injured_sum_Klasse, depth_Klasse, eq_magnitude_Klasse, hdi_Klasse FROM iket.damage, iket.event_earthquake, iket.location_data WHERE iket.damage.event_id=iket.event_earthquake.event_id AND iket.damage.location_id=iket.location_data.location_id";
		ResultSet result1 = statement.executeQuery(query);

		ResultSetMetaData resultmd = result1.getMetaData();
		int columnCount = resultmd.getColumnCount();

		result1.close();

		for (int j = 0; j <= columnCount - 1; j++) {

			String Spalte = spaltenname.get(j);

			String query2 = "SELECT "
					+ Spalte
					+ " FROM iket.damage, iket.event_earthquake, iket.location_data WHERE iket.damage.event_id=iket.event_earthquake.event_id AND iket.damage.location_id=iket.location_data.location_id GROUP BY "
					+ Spalte;
			ResultSet result2 = statement.executeQuery(query2);

			while (result2.next())
				unique_itemset.add(result2.getString(1));

		}
		
		return unique_itemset;

	}

	public static Integer db_size(Statement new_statement) throws Exception { 

		statement = new_statement;

		String query3 = "SELECT COUNT (*) FROM iket.damage, iket.event_earthquake, iket.location_data WHERE iket.damage.event_id=iket.event_earthquake.event_id AND iket.damage.location_id=iket.location_data.location_id";
		ResultSet result3 = statement.executeQuery(query3);

		while (result3.next()) {
			db_size = result3.getInt(1);
		}
		
		return db_size;
	}

}
